﻿using System;
using System.Collections.Generic;
using System.Text;
using SAPStudio.AvsFilterNet;
using System.IO;

[assembly: AvisynthFilterClass(typeof(SAPStudio.TimeCodeOutPut.TimeCodeOutPut), "TimeCodeOutPut", "csc+")]
namespace SAPStudio.TimeCodeOutPut
{
    public class TimeCodeOutPut : AvisynthFilter
    {
        String tcfilepath;
        Clip[] videos;
        int[] videosBound;
        //VideoInfo vi;

        public TimeCodeOutPut(AVSValue args, ScriptEnvironment env)
            : base(args, env)
        {
            // This is the constructor. It does not return any value, and is always used, 
            //  when an instance of the class is created.

            //The signature of constructor must be (AVSValue args, ScriptEnvironment env),
            //otherwise the loader won't recognize your plugin.

            //Save the parameter for future use.
            //Remember index 0 is the child clip saved in base class.

            tcfilepath = args[1].AsString();
            videos = new Clip[args[2].ArraySize()];
            videosBound = new int[args[2].ArraySize()];

            for (int i = 0; i < args[2].ArraySize(); i++ )
            {
                videos[i] = args[2][i].AsClip();
            }
            TCFileOutPut();
        }

        protected override void Dispose(bool disposing)
        {
            // This is where you can deallocate any memory you might have used.
            if (disposing)
            {
                //Release any managed resources here.
                //NOTE: You must dispose all Clips(except for the Child clip since it is disposed in base class) 
                //when they are no longer needed, otherwise avisynth may crash at exit.
                foreach (Clip c in videos)
                {
                    c.Dispose();
                }
            }

            //Release any unmanaged resources here.

            //Don't forget to call this
            base.Dispose(disposing);
        }

        public void TCFileOutPut()
        {            
            
            VideoInfo ori_vi = this.GetVideoInfo();
            //head of timecode v1 file, Assume FPS as the original clip
            String tclines = "# timecode format v1" + Environment.NewLine + "Assume " + (ori_vi.fps_numerator * 1.0 / ori_vi.fps_denominator) + Environment.NewLine;
            
            int i = 0;
            int offset = 0;
            foreach (Clip c in videos)
            {
                //get framecount and framerate, generate timecode line for this clip
                VideoInfo vi = c.GetVideoInfo();
                int framecount = vi.num_frames;
                double framerate =  vi.fps_numerator* 1.0/vi.fps_denominator;               
                String tcline = "" + offset + "," + (offset + framecount - 1) + "," + framerate + Environment.NewLine;
                tclines += tcline;
                //plus offset for the next clip
                offset += framecount;
                //remember the last position of each clip
                this.videosBound[i] = offset;
                i++;
            }
            //update to new framecount
            ori_vi.num_frames = this.videosBound[this.videosBound.Length - 1];
            this.SetVideoInfo(ref ori_vi);
            
            //write timecode to file
            try
            {
                FileStream tcFileFS = new FileStream(this.tcfilepath, FileMode.Create, FileAccess.Write);
                StreamWriter tcFileSW = new StreamWriter(tcFileFS);
                tcFileSW.Write(tclines);
                tcFileSW.Close();
            }
            catch (IOException e)
            {
                //error message, to do
                return ;
            }
            return ;
        }

        public override VideoFrame GetFrame(int n, ScriptEnvironment env)
        {    
            //find which clip is Frame n in.
            int videonum = this.getClipNumbyN(n);
            int relativeFrameNum = 0;
            if (videonum == -1)
            {
                env.ThrowError("TimeCodeOutPut: Error to index Frame n");
            }
            //calculate relative position
            if (videonum == 0)
            {
                relativeFrameNum = n;
            }
            else
            {
                relativeFrameNum = n - this.videosBound[videonum - 1];
            }
            //get the real videoframe
            VideoFrame f = null;
            f = this.videos[videonum].GetFrame(relativeFrameNum, env);
            return f;            
        }

        private int getClipNumbyN(int n)
        {
            if (n < 0)
            {
                return -1;
            }
            for (int i = 0; i < videosBound.Length; i++)
            {
                if (n < this.videosBound[i])
                {
                    return i;
                }
            }
            return -1;
        }
    }
}
